package com.qingshu.common.util.tree;

import java.util.ArrayList;
import java.util.List;

/**
 * 构建树型结构
 *
 * @author Huzz
 * @create 2022-07-25 18:36
 */
public class TreeBuilder<T> {
    private static final String SET_METHOD_PREFIX = "set";
    private static final String GET_METHOD_PREFIX = "get";

    private static final String DEFAULT_ID_NAME = "id";
    private static final String DEFAULT_PARENT_ID_NAME = "parentId";
    private static final String DEFAULT_CHILDREN_NAME = "children";

    /**
     * 构建树结构
     * @param rootId 根节点id
     * @param dataList 所有节点数据, 包含id|父级id|孩子 成员属性.
     * @return
     * @throws Exception
     */
    public static List builder(String rootId, List dataList) throws Exception {
        List tree = new ArrayList<>();
        for (Object data : dataList) {
            String parentId = data.getClass().getMethod(generateGetMethodName(DEFAULT_PARENT_ID_NAME)).invoke(data).toString();

            if (rootId.equals(parentId)) {
                // 是根节点的话, 递归查找以该节点id作为parentId的孩子
                List children = builder(data.getClass().getMethod(generateGetMethodName(DEFAULT_ID_NAME)).invoke(data).toString(), dataList);
                // 设置孩子
                data.getClass().getMethod(generateSetMethodName(DEFAULT_CHILDREN_NAME), List.class).invoke(data, children);
                // 保存当前节点
                tree.add(data);
            }
        }

        return tree;
    }

    /**
     * 构建树结构
     * @param rootId 根节点id
     * @param dataList 所有节点数据, 包含id|父级id|孩子 成员属性.
     * @param IDFiledName id字段名
     * @param parentIdFiledName 父级ID字段名
     * @param childrenFiledName 孩子字段名
     * @return
     * @throws Exception
     */
    public static List builder(String rootId, List dataList,String IDFiledName, String parentIdFiledName, String childrenFiledName) throws Exception {
        TreeBuilder treeBuilder = new TreeBuilder<>();
        return treeBuilder.getTree(rootId, dataList, IDFiledName, parentIdFiledName, childrenFiledName);
    }

    /**
     * 获取树
     * @param rootId 根节点id
     * @param dataList 所有节点数据, 包含id|父级id|孩子 成员属性.
     * @return
     * @throws Exception
     */
    public List<T> getTree(String rootId, List<T> dataList) throws Exception {
        return builder(rootId, dataList);
    }

    /**
     * 获取树
     * @param rootId 根节点id
     * @param dataList 所有节点数据, 包含id|父级id|孩子 成员属性.
     * @param IDFiledName id字段名
     * @param parentIdFiledName 父级ID字段名
     * @param childrenFiledName 孩子字段名
     * @return
     * @throws Exception
     */
    public List<T> getTree(String rootId, List<T> dataList, String IDFiledName, String parentIdFiledName, String childrenFiledName) throws Exception {
        List<T> tree = new ArrayList<>();
        for (T data : dataList) {
            String parentId = data.getClass().getMethod(generateGetMethodName(parentIdFiledName)).invoke(data).toString();

            if (rootId.equals(parentId)) {
                // 是根节点的话, 查找以该节点id作为parentId的孩子
                List<T> children = getTree(data.getClass().getMethod(generateGetMethodName(IDFiledName)).invoke(data).toString(), dataList, IDFiledName, parentIdFiledName, childrenFiledName);
                // 设置孩子
                data.getClass().getMethod(generateSetMethodName(childrenFiledName), List.class).invoke(data, children);
                // 保存当前节点
                tree.add(data);
            }
        }

        return tree;
    }

    /**
     * 获取所有父级数据集合
     * @param parentId 当前父级ID
     * @param dataList 集合
     * @return 只有父级组成的list
     * @throws Exception
     */
    public static List getAllParent(String parentId, List dataList) throws Exception {
        return getAllParent(parentId, dataList, DEFAULT_ID_NAME, DEFAULT_PARENT_ID_NAME);
    }

    /**
     * 获取所有父级数据集合
     * @param parentId 当前父级ID
     * @param dataList 集合
     * @param IDFiledName ID字段名称
     * @param parentIdFiledName 父级ID字段名称
     * @return 只有父级组成的list
     * @throws Exception
     */
    public static List getAllParent(String parentId, List dataList, String IDFiledName, String parentIdFiledName) throws Exception {
        List result = new ArrayList();
        for (Object data : dataList) {
            String id = data.getClass().getMethod(generateGetMethodName(IDFiledName)).invoke(data).toString();
            if (parentId.equals(id)) {
                result.add(data);
                getAllParent(data.getClass().getMethod(generateGetMethodName(parentIdFiledName)).invoke(data).toString(), dataList);
            }
        }
        return result;
    }

    /**
     * 获取当前节点所在路径
     * @param parentId 当前节点的父级id
     * @param dataList 集合
     * @param pathFiledName 要设置为路径名称的字段名称
     * @return 例: /根目录1/子目录2/子目录3 (当前: 子目录4)
     * @throws Exception
     */
    public static String getCurrentPath(String parentId, List dataList, String pathFiledName) throws Exception {
        return getCurrentPath(parentId, dataList, pathFiledName, DEFAULT_ID_NAME, DEFAULT_PARENT_ID_NAME);
    }

    /**
     * 获取当前节点所在路径
     * @param parentId 当前节点的父级id
     * @param dataList 集合
     * @param pathFiledName 要设置为路径名称的字段名称
     * @param IDFiledName ID字段名称
     * @param parentIdFiledName 父级ID字段名称
     * @return 例: /根目录1/子目录2/子目录3 (当前: 子目录4)
     * @throws Exception
     */
    public static String getCurrentPath(String parentId, List dataList, String pathFiledName, String IDFiledName, String parentIdFiledName) throws Exception {
        StringBuffer path = new StringBuffer();
        for (Object data : dataList) {
            String id = data.getClass().getMethod(generateGetMethodName(IDFiledName)).invoke(data).toString();
            if (parentId.equals(id)) {
                path.append("/" + data.getClass().getMethod(generateGetMethodName(pathFiledName)).invoke(data).toString());
                String p = getCurrentPath(data.getClass().getMethod(generateGetMethodName(parentIdFiledName)).invoke(data).toString(), dataList, pathFiledName);
                path.insert(0, p);
            }
        }
        return path.toString();
    }

    private static String generateGetMethodName(String filedName){
        return GET_METHOD_PREFIX +filedName.substring(0, 1).toUpperCase() + filedName.substring(1);
    }

    private static String generateSetMethodName(String filedName){
        return SET_METHOD_PREFIX +filedName.substring(0, 1).toUpperCase() + filedName.substring(1);
    }

}
